`timescale 1ns/1ps
`default_nettype none

/* NOTE:
* - 截取图像有效区域
* - 处理可能出现的丢包情况
*/

module pixel_crop (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // config
    input  wire [12:0]  I_cfg_pos_x, // x坐标，有符号
    input  wire [11:0]  I_cfg_pos_y, // y坐标，有符号
    input  wire [10:0]  I_cfg_col_num, // 带载列数（宽度）
    input  wire [10:0]  I_cfg_row_num, // 带载行数（高度）
    // input frame
    input  wire         I_frame_start,
    input  wire         I_frame_end,
    input  wire         I_row_end,
    input  wire         I_burst_start,
    input  wire [12:0]  I_burst_row,
    input  wire [12:0]  I_burst_col,
    input  wire         I_pixel_en,
    input  wire [7:0]   I_pixel_data,
    // output line
    output wire         O_line_start,  // 行开始
    output wire         O_line_end,    // 行结束
    output wire [9:0]   O_line_num,    // 行号
    output wire         O_pixel_valid, // 像素有效
    output wire [9:0]   O_pixel_col,   // 像素列坐标
    output wire [23:0]  O_pixel_gray   // 像素数据
);
//------------------------Parameter----------------------

//------------------------Local signal-------------------
// crop
wire [11:0] cfg_max_y;
reg         valid_burst; // 是否在截取区内
reg         row_changed; // 换行
reg  [13:0] row_num;
reg  [13:0] col_num;     // 输入像素在截取区内的列坐标，有符号
reg         valid_col;   // 当前像素有效
reg  [12:0] last_row;
reg  [2:0]  color;
reg  [7:0]  r_buf;
reg  [7:0]  g_buf;

// output line
reg         line_start;
reg         line_end;

//------------------------Instantiation------------------

//------------------------Body---------------------------
//{{{+++++++++++++++++++++crop+++++++++++++++++++++++++++
assign cfg_max_y = I_cfg_pos_y + I_cfg_row_num;

// valid_burst
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        valid_burst <= 1'b0;
    else if (I_frame_start || I_frame_end || I_row_end)
        valid_burst <= 1'b0;
    else if (I_burst_start) begin
        if ($signed({1'b0, I_burst_row}) < $signed(I_cfg_pos_y))
            valid_burst <= 1'b0;
        else if ($signed({1'b0, I_burst_row}) < $signed(cfg_max_y))
            valid_burst <= 1'b1;
        else
            valid_burst <= 1'b0;
    end
end

// row_changed
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        row_changed <= 1'b0;
    else if (I_frame_start)
        row_changed <= 1'b0;
    else if (I_burst_start && (I_burst_row != last_row || ~valid_burst))
        row_changed <= 1'b1;
    else
        row_changed <= 1'b0;
end

// row_num
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        row_num <= 1'b0;
    else if (I_burst_start)
        row_num <= $signed({1'b0, I_burst_row}) - $signed(I_cfg_pos_y);
end

// col_num
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        col_num <= 1'b0;
    else if (I_burst_start)
        col_num <= $signed({1'b0, I_burst_col}) - $signed(I_cfg_pos_x);
    else if (I_pixel_en && color[2])
        col_num <= col_num + 1'b1;
end

// valid_col
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        valid_col <= 1'b0;
    else if (~valid_burst)
        valid_col <= 1'b0;
    else if (I_pixel_en && color[0]) begin
        if ($signed(col_num) < $signed(1'b0))
            valid_col <= 1'b0;
        else if ($signed(col_num) < $signed({1'b0, I_cfg_col_num}))
            valid_col <= 1'b1;
        else
            valid_col <= 1'b0;
    end
end

// last_row
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        last_row <= 1'b0;
    else if (I_burst_start)
        last_row <= I_burst_row;
end

// color
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        color <= 3'b001;
    else if (I_burst_start)
        color <= 3'b001;
    else if (I_pixel_en)
        color <= {color[1:0], color[2]};

end

// r_buf
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        r_buf <= 1'b0;
    else if (I_pixel_en && color[0])
        r_buf <= I_pixel_data;
end

// g_buf
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        g_buf <= 1'b0;
    else if (I_pixel_en && color[1])
        g_buf <= I_pixel_data;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++output line++++++++++++++++++++
assign O_line_start  = line_start;
assign O_line_end    = line_end;
assign O_line_num    = row_num[9:0];
assign O_pixel_valid = valid_col & I_pixel_en & color[2];
assign O_pixel_col   = col_num[9:0];
assign O_pixel_gray  = {I_pixel_data, g_buf, r_buf};

// line_start
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        line_start <= 1'b0;
    else if (I_frame_start)
        line_start <= 1'b0;
    else if (row_changed && valid_burst)
        line_start <= 1'b1;
    else
        line_start <= 1'b0;
end

// line_end
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        line_end <= 1'b0;
    else if (I_frame_start)
        line_end <= 1'b0;
    else if (~valid_burst)
        line_end <= 1'b0;
    else if (I_frame_end || I_row_end)
        line_end <= 1'b1;
    else if (I_burst_start && (I_burst_row != last_row))
        line_end <= 1'b1;
    else
        line_end <= 1'b0;
end

//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
